<template>
    <a-scrollable ref="scrollableRef">
        <a-scrollable-h>
            <div
                class="a-table c-w-100 c-b"
                :class="[
                    `color-${formItemHook.myColor}`,
                    `size-${formItemHook.mySize}`,
                    { sharp: formItemHook.mySharp },
                    { disabled: formItemHook.myDisabled },
                    { 'un-top': state.scrollDistanceTop > 0 },
                    { 'un-left': state.scrollDistanceLeft > 0 },
                    { 'un-right': state.scrollDistanceRight > 0 },
                ]"
                :style="tableContainerStyle"
            >
                <table :style="tableStyle" ref="tableRef">
                    <thead
                        :class="[
                            {
                                'sticky-top': !!props.maxHeight,
                            },
                        ]"
                    >
                        <tr>
                            <template v-if="props.selectable">
                                <th
                                    v-if="isMultiSelect"
                                    @click="methods.toggleSelectAll"
                                    class="select-cell selectable"
                                    :class="[
                                        {
                                            'sticky-left': props.fixLeft,
                                        },
                                    ]"
                                    :rowspan="`${headerRows.length}`"
                                    :style="{
                                        width: state.actionWidth + 'px',
                                        left: selectCellLeft,
                                    }"
                                >
                                    <span class="fa fa-fw fa-square-o" v-show="!anySelected"></span>
                                    <span
                                        class="fa fa-fw fa-minus-square-o"
                                        v-show="anySelected && !allSelected"
                                    ></span>
                                    <span class="fa fa-fw fa-check-square-o" v-show="allSelected"></span>
                                </th>
                                <th
                                    v-else
                                    class="select-cell"
                                    :class="[
                                        {
                                            'sticky-left': props.fixLeft,
                                        },
                                    ]"
                                    :rowspan="`${headerRows.length}`"
                                    :style="{
                                        width: state.actionWidth + 'px',
                                        left: selectCellLeft,
                                    }"
                                ></th>
                            </template>
                            <template v-if="props.expandable">
                                <th
                                    v-if="isMultiExpand"
                                    @click="methods.toggleExpandAll"
                                    class="expand-cell expandable"
                                    :class="[
                                        {
                                            'sticky-left': props.fixLeft,
                                        },
                                    ]"
                                    :rowspan="`${headerRows.length}`"
                                    :style="{
                                        width: state.actionWidth + 'px',
                                        left: expandCellLeft,
                                    }"
                                >
                                    <span class="fa fa-fw fa-angle-double-right" v-show="!anyExpand"></span>
                                    <span class="fa fa-fw fa-minus-square-o" v-show="anyExpand && !allExpand"></span>
                                    <span class="fa fa-fw fa-angle-double-down" v-show="allExpand"></span>
                                </th>
                                <th
                                    v-else
                                    class="expand-cell"
                                    :class="[
                                        {
                                            'sticky-left': props.fixLeft,
                                        },
                                    ]"
                                    :rowspan="`${headerRows.length}`"
                                    :style="{
                                        width: state.actionWidth + 'px',
                                        left: expandCellLeft,
                                    }"
                                ></th>
                            </template>
                            <th
                                v-if="props.draggable"
                                class="drag-cell"
                                :class="[
                                    {
                                        'sticky-left': props.fixLeft,
                                    },
                                ]"
                                :rowspan="`${headerRows.length}`"
                                :style="{
                                    width: state.actionWidth + 'px',
                                    left: dragCellLeft,
                                }"
                            ></th>
                            <th
                                v-for="header in firstHeaderRow"
                                :width="
                                    headerRows.length === 1
                                        ? `${(props.getHeaderWidthFunc(header.item) / headerTotalWidth) * 100}%`
                                        : 'unset'
                                "
                                :rowspan="`${header.rowSpan}`"
                                :colspan="`${header.colSpan}`"
                                class="header-cell normal-cell"
                                :class="[
                                    {
                                        'right-cell': rightHeaderSet.has(header.item),
                                        'sticky-left': props.fixLeft && leftHeaderSet.has(header.item),
                                        'sticky-right': props.fixRight && rightHeaderSet.has(header.item),
                                    },
                                ]"
                                :style="{
                                    left: normalSellLeft,
                                }"
                            >
                                <slot :name="`header-${props.getHeaderNameFunc(header.item)}`" :header="header.item">
                                    {{ props.getHeaderTextFunc(header.item) }}
                                </slot>
                            </th>
                        </tr>
                        <tr v-for="(row, i) in restHeaderRows">
                            <th
                                v-for="header in row"
                                :width="
                                    i === restHeaderRows.length - 1
                                        ? `${(props.getHeaderWidthFunc(header.item) / headerTotalWidth) * 100}%`
                                        : 'unset'
                                "
                                :rowspan="`${header.rowSpan}`"
                                :colspan="`${header.colSpan}`"
                                :class="[
                                    {
                                        'right-cell': rightHeaderSet.has(header.item),
                                        'sticky-left': fixLeft && leftHeaderSet.has(header.item),
                                        'sticky-right': fixRight && rightHeaderSet.has(header.item),
                                    },
                                ]"
                            >
                                <slot :name="`header-${props.getHeaderNameFunc(header.item)}`" :header="header.item">
                                    {{ props.getHeaderTextFunc(header.item) }}
                                </slot>
                            </th>
                        </tr>
                    </thead>
                    <tbody>
                        <template v-for="(data, dataIndex) in props.dataList" :key="props.getKeyFunc(data)">
                            <tr
                                class="data-row"
                                :class="[
                                    {
                                        seleced: methods.isItemSelected(data),
                                        dragging: dataIndex === state.draggingIndex,
                                        'one-of-us-dragging': state.draggingIndex !== undefined,
                                    },
                                    dataIndex === state.draggingIndex ? `c-shadow-${formItemHook.mySize}` : '',
                                ]"
                            >
                                <template v-if="props.selectable">
                                    <td
                                        v-if="props.getCanBeSelectedFunc(data)"
                                        @click="methods.toggleSelectItem(data)"
                                        class="select-cell selectable"
                                        :class="[{ 'sticky-left': props.fixLeft }]"
                                        :style="{
                                            width: state.actionWidth + 'px',
                                            left: selectCellLeft,
                                        }"
                                    >
                                        <template v-if="isMultiSelect">
                                            <span
                                                v-if="methods.isItemSelected(data)"
                                                class="fa fa-fw fa-check-square-o"
                                            ></span>
                                            <span v-else class="fa fa-fw fa-square-o"></span>
                                        </template>
                                        <template v-else>
                                            <span v-if="methods.isItemSelected(data)" class="fa fa-fw fa-circle"></span>
                                            <span v-else class="fa fa-fw fa-circle-o"></span>
                                        </template>
                                    </td>
                                    <td
                                        v-else
                                        class="select-cell"
                                        :class="[{ 'sticky-left': props.fixLeft }]"
                                        :style="{
                                            width: state.actionWidth + 'px',
                                            left: selectCellLeft,
                                        }"
                                    ></td>
                                </template>
                                <td
                                    v-if="props.expandable"
                                    class="expand-cell expandable"
                                    :class="[
                                        {
                                            'sticky-left': props.fixLeft,
                                        },
                                    ]"
                                    :style="{
                                        width: state.actionWidth + 'px',
                                        left: expandCellLeft,
                                    }"
                                    @click="methods.toggleExpandItem(data)"
                                >
                                    <span v-if="methods.isItemExpand(data)" class="fa fa-fw fa-angle-down"></span>
                                    <span v-else class="fa fa-fw fa-angle-right"></span>
                                </td>
                                <td
                                    v-if="props.draggable"
                                    class="drag-cell draggable"
                                    :class="[
                                        {
                                            'sticky-left': props.fixLeft,
                                        },
                                    ]"
                                    :style="{
                                        width: state.actionWidth + 'px',
                                        left: dragCellLeft,
                                    }"
                                    @mousedown.prevent="methods.dragStart(dataIndex, $event as DragEvent)"
                                >
                                    <span class="fa fa-fw fa-arrows"></span>
                                </td>
                                <td
                                    v-for="(header, headerIndex) in lastHeaderRow"
                                    :width="`${(props.getHeaderWidthFunc(header.item) / headerTotalWidth) * 100}%`"
                                    class="data-cell normal-cell"
                                    :class="[
                                        {
                                            'sticky-left': props.fixLeft && headerIndex === 0,
                                            'sticky-right': props.fixRight && headerIndex === lastHeaderRow.length - 1,
                                        },
                                    ]"
                                    :style="{
                                        left: normalSellLeft,
                                    }"
                                >
                                    <slot
                                        :name="`data-${props.getHeaderNameFunc(header.item)}`"
                                        :header="header"
                                        :headerIndex="headerIndex"
                                        :data="data"
                                        :dataIndex="dataIndex"
                                    >
                                        {{ data[getHeaderNameFunc(header.item)] }}
                                    </slot>
                                </td>
                            </tr>
                            <tr
                                v-if="props.expandable && methods.isItemExpand(data)"
                                class="expand-body-row"
                                :class="[
                                    {
                                        dragging: dataIndex === state.draggingIndex,
                                        'one-of-us-dragging': state.draggingIndex !== undefined,
                                    },
                                    dataIndex === state.draggingIndex ? `c-shadow-${formItemHook.mySize}` : '',
                                ]"
                            >
                                <td class="expand-body-cell" :colspan="`${expandBodyCellColSpan}`">
                                    <slot name="expandRow" :data="data" :dataIndex="dataIndex"></slot>
                                </td>
                            </tr>
                        </template>
                    </tbody>
                </table>
                <slot name="emptyTips" v-if="!props.dataList.length">
                    <div class="c-p-v-m c-w-100 c-align-center">
                        {{ $t('ATable.Empty') }}
                    </div>
                </slot>
            </div>
        </a-scrollable-h>
    </a-scrollable>
</template>

<style lang="scss" scoped>
@import '../scss/vars';

.a-table {
    table {
        border-collapse: collapse;
        table-layout: fixed;
    }

    thead {
        &.sticky-top {
            position: sticky;
            top: 0;
        }
    }

    tbody tr {
        &.seleced {
            th,
            td:not(.expand-body-cell) {
                background-color: #eaeaea;
            }
        }

        &:not(.seleced) {
            &:nth-child(even) {
                th,
                td:not(.expand-body-cell) {
                    background-color: #fafafa;
                }
            }

            &:hover {
                th,
                td:not(.expand-body-cell) {
                    background-color: #f5f5f5;
                }
            }
        }

        &:not(:last-child) {
            td {
                border-bottom: solid 1px $defaultBorderColor;
            }
        }

        &.one-of-us-dragging:not(.dragging) {
            transition: transform 200ms;
        }

        &.dragging {
            position: relative;
            z-index: 1;
            opacity: 0.8;
        }
    }

    th,
    td {
        background-color: $defaultBgColor;
        text-align: left;
        box-sizing: border-box;

        &.sticky-left {
            position: sticky;
        }

        &.sticky-right {
            position: sticky;
            right: 0;
        }
    }

    th {
        border-bottom: solid 1px $defaultBorderColor;

        &:not(.right-cell) {
            border-right: solid 1px $defaultBorderColor;
        }
    }

    td {
        &:not(:last-child) {
            border-right: solid 1px $defaultBorderColor;
        }
    }

    &.un-top {
        thead {
            &.sticky-top {
                box-shadow: 0 1px 10px $defaultShadowColor;
            }
        }
    }

    &.un-left {
        th,
        td {
            &.sticky-left {
                &::before {
                    content: '';
                    display: block;
                    position: absolute;
                    right: -10px;
                    top: 0;
                    width: 10px;
                    height: 100%;
                    box-shadow: inset 10px 0 10px -10px $defaultShadowColor;
                }
            }
        }
    }

    &.un-right {
        th,
        td {
            &.sticky-right {
                &::before {
                    content: '';
                    display: block;
                    position: absolute;
                    left: -10px;
                    top: 0;
                    width: 10px;
                    height: 100%;
                    box-shadow: inset -10px 0 10px -10px $defaultShadowColor;
                }
            }
        }
    }

    @each $sizePackageName, $sizePackageValue in $sizePackages {
        &.size-#{$sizePackageName} {
            th,
            td:not(.expand-body-cell) {
                padding-left: map-get($sizePackageValue, 'space');
                padding-right: map-get($sizePackageValue, 'space');
                height: map-get($sizePackageValue, 'size');
                font-size: map-get($sizePackageValue, 'fontSize');
            }
        }
    }

    .select-cell {
        padding: 0;

        &.selectable {
            text-align: center;
            cursor: pointer;
        }
    }

    .expand-cell {
        padding: 0;

        &.expandable {
            text-align: center;
            cursor: pointer;
        }
    }

    .drag-cell {
        padding: 0;

        &.draggable {
            text-align: center;

            &:not(.disabled) {
                cursor: move;
            }

            &.disabled {
                opacity: 0.5;
                cursor: not-allowed;
            }
        }
    }
}
</style>

<script setup lang="ts">
import { InputPropsType, KeyType, PopupExposed, Rule_maxLength, Rule_minLength } from '@/types'
import AScrollable from './AScrollable.vue'
import AScrollableH from './AScrollableH.vue'
import utils from '@/common/utils'
import { computed, nextTick, onMounted, onUnmounted, reactive, ref, watch } from 'vue'
import { useFormItem } from '@/hooks/useFormItem'
import { $t } from '@/lang'
import { useInput } from '@/hooks/useInput'

interface PropsType extends InputPropsType<any[], Rule_minLength | Rule_maxLength> {
    headerList: any[]
    getHeaderNameFunc?: (a: any) => string
    getHeaderTextFunc?: (a: any) => string
    getHeaderWidthFunc?: (a: any) => number
    getSubHeadersFunc?: (a: any) => any[]
    dataList?: any[]
    selectable?: boolean
    /**
     * 选择方式，可选multi、single，默认multi，注意，即使为single方式，update:value事件的值也是数组
     */
    selectType?: 'multi' | 'single'
    getValueFunc?: (a: any) => any
    getKeyFunc?: (a: any) => KeyType
    maxHeight?: number
    getCanBeSelectedFunc?: (a: any) => boolean
    tableWidth?: number
    fixLeft?: boolean
    fixRight?: boolean
    expandable?: boolean
    /**
     * 展开方式，可选multi、single，默认multi，注意，即使为single方式，update:expandValue事件的值也是数组
     */
    expandType?: 'multi' | 'single'
    expandValue?: any[]
    draggable?: boolean
}
const props = withDefaults(defineProps<PropsType>(), {
    value: undefined,
    color: undefined,
    disabled: undefined,
    size: undefined,
    sharp: undefined,
    rules: () => [],
    isInner: false,
    headerList: () => [],
    getHeaderNameFunc: (a: any) => a.name,
    getHeaderTextFunc: (a: any) => a.text,
    getHeaderWidthFunc: (a: any) => a.width ?? 10,
    getSubHeadersFunc: (a: any) => a.subHeaders,
    dataList: () => [],
    selectable: false,
    selectType: 'multi',
    getValueFunc: (a: any) => a.id,
    getKeyFunc: (a: any) => a.id,
    maxHeight: undefined,
    getCanBeSelectedFunc: (_: any) => true,
    tableWidth: undefined,
    fixLeft: false,
    fixRight: false,
    expandable: false,
    expandType: 'multi',
    expandValue: () => [],
    draggable: false,
})

interface StateType {
    actionWidth: number
    scrollDistanceTop: number
    scrollDistanceRight: number
    scrollDistanceBottom: number
    scrollDistanceLeft: number
    draggingIndex?: number
    startMouseY?: number
    startTranslateY?: number
    lastMoveOnIndex?: number
    trList: any[]
}
const state = reactive<StateType>({
    actionWidth: 60,
    scrollDistanceTop: 60,
    scrollDistanceRight: 60,
    scrollDistanceBottom: 60,
    scrollDistanceLeft: 60,
    draggingIndex: undefined,
    startMouseY: undefined,
    startTranslateY: undefined,
    lastMoveOnIndex: undefined,
    trList: [],
})

const valueSet = computed(() => {
    return new Set(props.value)
})
const anySelected = computed(() => {
    return props.dataList.length && props.dataList.some(a => valueSet.value.has(props.getValueFunc(a)))
})
const allSelected = computed(() => {
    return props.dataList.length && props.dataList.every(a => valueSet.value.has(props.getValueFunc(a)))
})
const isMultiSelect = computed(() => {
    return props.selectType === 'multi'
})
const tableContainerStyle = computed(() => {
    if (props.maxHeight) {
        return {
            maxHeight: props.maxHeight + 'px',
        }
    }
    return {}
})
const tableStyle = computed(() => {
    if (props.tableWidth) {
        return {
            width: props.tableWidth + 'px',
        }
    }
    return {
        width: '100%',
    }
})
const headerInfo = computed(() => {
    let totalWidth = 0

    let getColSpan = (header: any): number => {
        let subHeaders = props.getSubHeadersFunc(header)
        if (subHeaders === undefined || subHeaders.length === 0) {
            return 1
        }
        return subHeaders.reduce((total, a) => total + getColSpan(a), 0)
    }
    let root = {
        parent: undefined,
        item: {
            subHeaders: [...props.headerList],
        },
        subItemList: [],
        rowSpan: 1,
        colSpan: 1,
    }
    let sourceItemList = [root] as any[]
    let rows = [] as any[]
    let firstItem = root
    let lastItem = root
    let lastHeaderRow = [] as any[]
    while (true) {
        let currentItem = sourceItemList.shift()!

        currentItem.colSpan = getColSpan(currentItem.item)

        let subHeaders = props.getSubHeadersFunc(currentItem.item)
        if (subHeaders?.length ?? 0 > 0) {
            let subItemList = subHeaders.map(a => {
                return {
                    parent: currentItem,
                    item: a,
                    subItemList: [],
                    rowSpan: 1,
                    colSpan: 1,
                }
            })
            currentItem.subItemList = subItemList
            sourceItemList.push(...subItemList)
        } else {
            totalWidth += props.getHeaderWidthFunc(currentItem.item)
            lastHeaderRow.push(currentItem)
        }

        if (currentItem === firstItem) {
            rows.forEach((row: any[]) => {
                row.forEach(a => {
                    if (a.subItemList.length === 0) {
                        a.rowSpan++
                    }
                })
            })
            rows.push([])
        }

        rows[rows.length - 1].push(currentItem)

        if (sourceItemList.length === 0) {
            break
        }

        if (currentItem === lastItem) {
            firstItem = sourceItemList[0]
            lastItem = sourceItemList[sourceItemList.length - 1]
        }
    }
    rows.shift()

    let leftHeaderList = [] as any[]
    let leftParentHeader = props.headerList[0]
    while (true) {
        leftHeaderList.push(leftParentHeader)
        let subHeaders = props.getSubHeadersFunc(leftParentHeader)
        if (subHeaders === undefined || subHeaders.length === 0) {
            break
        }
        leftParentHeader = subHeaders[subHeaders[0]]
    }

    let rightHeaderList = []
    let rightParentHeader = props.headerList[props.headerList.length - 1]
    while (true) {
        rightHeaderList.push(rightParentHeader)
        let subHeaders = props.getSubHeadersFunc(rightParentHeader)
        if (subHeaders === undefined || subHeaders.length === 0) {
            break
        }
        rightParentHeader = subHeaders[subHeaders.length - 1]
    }

    return {
        rows,
        totalWidth,
        lastHeaderRow,
        leftHeaderList,
        rightHeaderList,
    }
})
const headerTotalWidth = computed(() => {
    return headerInfo.value.totalWidth
})
const headerRows = computed(() => {
    return headerInfo.value.rows
})
const firstHeaderRow = computed(() => {
    return headerRows.value[0]
})
const restHeaderRows = computed(() => {
    return headerRows.value.slice(1)
})
const lastHeaderRow = computed(() => {
    return headerInfo.value.lastHeaderRow
})
const leftHeaderList = computed(() => {
    return headerInfo.value.leftHeaderList
})
const leftHeaderSet = computed(() => {
    return new Set(leftHeaderList.value)
})
const rightHeaderList = computed(() => {
    return headerInfo.value.rightHeaderList
})
const rightHeaderSet = computed(() => {
    return new Set(rightHeaderList.value)
})
const expandValueSet = computed(() => {
    return new Set(props.expandValue)
})
const anyExpand = computed(() => {
    return props.dataList.some(a => expandValueSet.value.has(props.getValueFunc(a)))
})
const allExpand = computed(() => {
    return props.dataList.every(a => expandValueSet.value.has(props.getValueFunc(a)))
})
const isMultiExpand = computed(() => {
    return props.expandType === 'multi'
})
const expandBodyCellColSpan = computed(() => {
    let n = lastHeaderRow.value.length
    if (props.selectable) {
        n += 1
    }
    if (props.expandable) {
        n += 1
    }
    if (props.draggable) {
        n += 1
    }
    return n
})
const selectCellLeft = computed(() => {
    return 0
})
const expandCellLeft = computed(() => {
    let n = 0
    if (props.selectable) {
        n += 1
    }
    return n * state.actionWidth + 'px'
})
const dragCellLeft = computed(() => {
    let n = 0
    if (props.selectable) {
        n += 1
    }
    if (props.expandable) {
        n += 1
    }
    return n * state.actionWidth + 'px'
})
const normalSellLeft = computed(() => {
    let n = 0
    if (props.selectable) {
        n += 1
    }
    if (props.expandable) {
        n += 1
    }
    if (props.draggable) {
        n += 1
    }
    return n * state.actionWidth + 'px'
})

const emit = defineEmits<{
    (e: 'update:value', value: any[]): void
    (e: 'update:expandValue', value: any[]): void
    (e: 'dragEnd', value: any[]): void
}>()

useInput(props)
const formItemHook = useFormItem(props)

const tableRef = ref<HTMLTableElement>()
const scrollableRef = ref<PopupExposed>()
watch(
    () => props.dataList,
    async () => {
        await nextTick()
        Array.from(tableRef.value!.querySelectorAll(':scope > tbody > tr')).forEach(
            a => ((a as HTMLElement).style.transform = ''),
        )
    },
)
onMounted(() => {
    methods.calcScrollDistance()
    let containerEl = utils.getEl(scrollableRef.value!.getTargetEl())!
    containerEl.addEventListener('scroll', methods.calcScrollDistance)
    window.document.addEventListener('mouseup', methods.onDocumentMouseup)
    window.document.addEventListener('mousemove', methods.onDocumentMousemove)
})
onUnmounted(() => {
    window.document.removeEventListener('mouseup', methods.onDocumentMouseup)
    window.document.removeEventListener('mousemove', methods.onDocumentMousemove)
})

const methods = {
    input(val: any[]) {
        emit('update:value', val)
    },
    isItemSelected(item: any) {
        let itemValue = props.getValueFunc(item)
        return valueSet.value.has(itemValue)
    },
    toggleSelectItem(item: any) {
        let itemValue = props.getValueFunc(item)
        if (isMultiSelect.value) {
            if (!this.isItemSelected(item)) {
                this.input([...props.value, itemValue])
            } else {
                this.input(props.value.filter(a => a !== itemValue))
            }
        } else {
            this.input([itemValue])
        }
    },
    toggleSelectAll() {
        if (anySelected.value) {
            let dataListValueSet = new Set(props.dataList.map(a => props.getValueFunc(a)))
            this.input(props.value.filter(a => !dataListValueSet.has(a)))
        } else {
            let newValue = [...props.value]
            props.dataList.forEach(a => {
                if (props.getCanBeSelectedFunc(a)) {
                    let value = props.getValueFunc(a)
                    if (!valueSet.value.has(value)) {
                        newValue.push(value)
                    }
                }
            })
            this.input(newValue)
        }
    },
    calcScrollDistance() {
        let containerEl = utils.getEl(scrollableRef.value!.getTargetEl())!
        state.scrollDistanceTop = containerEl.scrollTop
        state.scrollDistanceRight = containerEl.scrollWidth - containerEl.clientWidth - containerEl.scrollLeft
        state.scrollDistanceBottom = containerEl.scrollHeight - containerEl.clientHeight - containerEl.scrollTop
        state.scrollDistanceLeft = containerEl.scrollLeft
    },
    inputExpand(val: any[]) {
        emit('update:expandValue', val)
    },
    isItemExpand(item: any) {
        let itemValue = props.getValueFunc(item)
        return expandValueSet.value.has(itemValue)
    },
    toggleExpandItem(item: any) {
        let itemValue = props.getValueFunc(item)
        if (isMultiExpand.value) {
            if (!this.isItemExpand(item)) {
                this.inputExpand([...props.expandValue, itemValue])
            } else {
                this.inputExpand(props.expandValue.filter(a => a !== itemValue))
            }
        } else {
            if (!this.isItemExpand(item)) {
                this.inputExpand([itemValue])
            } else {
                this.inputExpand([])
            }
        }
    },
    toggleExpandAll() {
        if (anyExpand.value) {
            let dataListValueSet = new Set(props.dataList.map(a => props.getValueFunc(a)))
            this.inputExpand(props.expandValue.filter(a => !dataListValueSet.has(a)))
        } else {
            let newValue = [...props.expandValue]
            props.dataList.forEach(a => {
                let value = props.getValueFunc(a)
                if (!expandValueSet.value.has(value)) {
                    newValue.push(value)
                }
            })
            this.inputExpand(newValue)
        }
    },
    getTranslateY(el: HTMLElement) {
        const str = el.style.transform?.match(/-?\d+/)
        if (str) {
            return Number(str)
        }
        return 0
    },
    dragStart(dataIndex: number, e: DragEvent) {
        state.startMouseY = e.clientY
        state.draggingIndex = dataIndex
        state.trList = Array.from(tableRef.value!.querySelectorAll(':scope > tbody > tr.data-row')).map(a => {
            let dataRowRect = a.getBoundingClientRect()
            let obj = {
                dataRowEl: a,
                rect: {
                    top: dataRowRect.top,
                    bottom: dataRowRect.bottom,
                    height: dataRowRect.height,
                },
            } as any
            let expandBodyRowEl = a.nextElementSibling
            if (expandBodyRowEl && expandBodyRowEl.classList.contains('expand-body-row')) {
                obj.expandBodyRowEl = expandBodyRowEl
                let expandBodyRowRect = expandBodyRowEl.getBoundingClientRect()
                obj.rect.height += expandBodyRowRect.height
                obj.rect.bottom = expandBodyRowRect.bottom
            }
            return obj
        })
        let draggingTrEl = state.trList[state.draggingIndex].dataRowEl
        state.startTranslateY = methods.getTranslateY(draggingTrEl)
    },
    onDocumentMousemove(e: MouseEvent) {
        if (state.draggingIndex === undefined) {
            return
        }
        let draggingTrEl = state.trList[state.draggingIndex].dataRowEl
        draggingTrEl.style.transform = `translateY(${e.clientY - state.startMouseY! + state.startTranslateY!}px)`
        let draggingExpandBodyRowEl = state.trList[state.draggingIndex].expandBodyRowEl
        if (draggingExpandBodyRowEl) {
            draggingExpandBodyRowEl.style.transform = `translateY(${
                e.clientY - state.startMouseY! + state.startTranslateY!
            }px)`
        }
        let moveOnIndex = state.trList.findIndex(a => a.rect.top <= e.clientY && a.rect.bottom >= e.clientY)
        let lastMoveOnIndex = state.lastMoveOnIndex
        state.lastMoveOnIndex = moveOnIndex
        if (moveOnIndex === -1 || moveOnIndex === state.draggingIndex || moveOnIndex === lastMoveOnIndex) {
            return
        }
        let moveOnTrEl = state.trList[moveOnIndex].dataRowEl
        let draggingRect = state.trList[state.draggingIndex].rect
        let moveOnRect = state.trList[moveOnIndex].rect

        let oldMoveOnRectTop = moveOnRect.top
        if (draggingRect.top > moveOnRect.top) {
            let oldDraggingRectBottom = draggingRect.bottom
            draggingRect.top = moveOnRect.top
            draggingRect.bottom = draggingRect.top + draggingRect.height
            moveOnRect.bottom = oldDraggingRectBottom
            moveOnRect.top = moveOnRect.bottom - moveOnRect.height
        } else {
            let oldMoveOnRectBottom = moveOnRect.bottom
            moveOnRect.top = draggingRect.top
            moveOnRect.bottom = moveOnRect.top + moveOnRect.height
            draggingRect.bottom = oldMoveOnRectBottom
            draggingRect.top = draggingRect.bottom - draggingRect.height
        }
        let oldTranslateY = this.getTranslateY(moveOnTrEl)
        let newTranslateY = moveOnRect.top - oldMoveOnRectTop + oldTranslateY
        moveOnTrEl.style.transform = `translateY(${newTranslateY}px)`
        let moveOnExpandBodyRowEl = state.trList[moveOnIndex].expandBodyRowEl
        if (moveOnExpandBodyRowEl) {
            moveOnExpandBodyRowEl.style.transform = `translateY(${newTranslateY}px)`
        }
    },
    onDocumentMouseup() {
        if (state.draggingIndex === undefined) {
            return
        }
        let draggingTrEl = state.trList[state.draggingIndex].dataRowEl
        let draggingRect = state.trList[state.draggingIndex].rect
        let realRect = draggingTrEl.getBoundingClientRect()
        let oldTranslateY = this.getTranslateY(draggingTrEl)
        let newTranslateY = draggingRect.top - realRect.top + oldTranslateY
        draggingTrEl.style.transform = `translateY(${newTranslateY}px)`
        let draggingExpandBodyRowEl = state.trList[state.draggingIndex].expandBodyRowEl
        if (draggingExpandBodyRowEl) {
            draggingExpandBodyRowEl.style.transform = `translateY(${newTranslateY}px)`
        }
        let listToResort = state.trList.map((a, i) => {
            return {
                index: i,
                rect: a.rect,
            }
        })
        listToResort.sort((a, b) => a.rect.top - b.rect.top)
        let newDataList = listToResort.map(a => props.dataList[a.index])

        state.draggingIndex = undefined
        state.startMouseY = undefined
        state.lastMoveOnIndex = undefined
        state.trList = []

        emit('dragEnd', newDataList)
    },
}
</script>
